﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once

#include "GcmpDevService.h"
#include "StringUtil.h"
#include "OwnerPtr.h"

#include <iostream>
#ifndef _WINDOWS
#include <dirent.h>
#else
#include <io.h>
#include <Windows.h>
#endif


namespace gcmp
{
    struct FileDeleter
    {
        void operator()(FILE* p) const
        {
            fclose(p);
        };
    };

    typedef OwnerPtr<FILE, FileDeleter> FileOwnerPtr;

    namespace FileSystem
    {
        // 文件路径分隔符
        FORCE_INLINE const std::wstring Separator()
        {
#ifdef _WINDOWS
            const std::wstring s_separator = L"\\";
#else
            const std::wstring s_separator = L"/";
#endif
            return s_separator;
        }

        FORCE_INLINE static const wchar_t SeparatorChar()
        {
#ifdef _WINDOWS
            return L'\\';
#else
            return L'/';
#endif
        }

        FORCE_INLINE std::wstring GetExeDirPath()
        {
#ifdef _WINDOWS
            wchar_t pPath[MAX_PATH];
            GetModuleFileName(NULL, pPath, MAX_PATH);

            std::wstring wsPath = pPath;
            return wsPath.substr(0, wsPath.find_last_of(FileSystem::SeparatorChar()) + 1);
#else
            return L"./";
#endif
        }

        FORCE_INLINE std::wstring GetCurrentProcessID()
        {
#ifdef _WINDOWS
            DWORD pid = GetCurrentProcessId();
            return StringUtil::ToWString(pid);
#else
            //暂不实现除windows系统外的其他系统
            return L"";
#endif
        }

        GCMP_DEV_SERVICE_EXPORT std::wstring GetSdkDirPath();

        // 创建目录，不能一次递归创建多级
        // 如果名字是CreateDirectory，VS2010报错，暂定为CreateDir
        // param showDebugWarning，出错时是否弹出调试警告，默认弹出；调用者可自行控制
        GCMP_DEV_SERVICE_EXPORT bool CreateDir(const std::wstring &path, bool showDebugWarning = true);
        // 创建目录，可以一次递归创建多级。
        // param showDebugWarning，出错时是否弹出调试警告，默认弹出；调用者可自行控制
        GCMP_DEV_SERVICE_EXPORT bool CreateDirectories(const std::wstring &path, bool showDebugWarning = true);

        // 临时文件目录，以文件分隔符结尾
        GCMP_DEV_SERVICE_EXPORT std::wstring GetTempDirPath();
        // <user name>\Local Settings\Applicaiton Data (non roaming)\AppGbmp\
        // 以文件分隔符结尾
        GCMP_DEV_SERVICE_EXPORT std::wstring GetLocalAppDataDirPath(const std::wstring& appName = L"AppGbmp");
        // <user name>\Applicaiton Data (non roaming)
                // 以文件分隔符结尾
        GCMP_DEV_SERVICE_EXPORT std::wstring GetAppDataDirPath();

        // 获取工作目录
        GCMP_DEV_SERVICE_EXPORT std::wstring GetWorkingDirPath();

        // 获取测试目录
        GCMP_DEV_SERVICE_EXPORT std::wstring GetTestDirPath();

        /// \brief 获取系统磁盘下的ProgramData文件目录
        ///
        /// \return 路径的字符串
        GCMP_DEV_SERVICE_EXPORT std::wstring GetCommonAppDataDirPath();

        // 设置工作目录
        GCMP_DEV_SERVICE_EXPORT void SetWorkingDirPath(const std::wstring& path);

        GCMP_DEV_SERVICE_EXPORT FileOwnerPtr OpenFile(const std::wstring &path, const std::wstring &mode);

        /// \brief 打开指定文件，如果出错会将出错信息放入日志
        /// \param path 文件路径
        /// \param mode 打开文件的方式
        /// \return 返回文件文件指针
        GCMP_DEV_SERVICE_EXPORT FileOwnerPtr OpenFileWithLog(const std::wstring& path, const std::wstring& mode);

        GCMP_DEV_SERVICE_EXPORT bool OpenStream(std::fstream &stream, const std::wstring &path, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out);
        GCMP_DEV_SERVICE_EXPORT bool OpenStream(std::ifstream &stream, const std::wstring &path, std::ios_base::openmode mode = std::ios_base::in);
        GCMP_DEV_SERVICE_EXPORT bool OpenStream(std::ofstream &stream, const std::wstring &path, std::ios_base::openmode mode = std::ios_base::out);
        GCMP_DEV_SERVICE_EXPORT bool OpenStream(std::wofstream &stream, const std::wstring &path, std::ios_base::openmode mode = std::ios_base::out);

        GCMP_DEV_SERVICE_EXPORT bool RemoveFile(const std::wstring &path);
        // 只能删除空目录
        // 如果名字是RemoveDirectory，VS2010报错，暂定为RemoveDir
        GCMP_DEV_SERVICE_EXPORT bool RemoveDir(const std::wstring &path);
        // 重命名文件或目录
        GCMP_DEV_SERVICE_EXPORT bool Rename(const std::wstring &oldName, const std::wstring &newName);

        /// \brief 重命名文件或目录,并返回错误代码
        /// \param oldName 原始文件名
        /// \param newName 新文件名
        /// \return 在Windows下使用的是GetLastError的错误码，在其他操作系统下，使用的是errno的错误码
        GCMP_DEV_SERVICE_EXPORT INT32 RenameReturnErrorCode(const std::wstring& oldName, const std::wstring& newName);

        // 统一路径分隔符，去除多余路径分隔符
        GCMP_DEV_SERVICE_EXPORT std::wstring GetPurePath(const std::wstring &path);

        typedef void* ModuleHandle;
        // moduldPath - 绝对路径或者相对于exe的相对路径。
        GCMP_DEV_SERVICE_EXPORT ModuleHandle LoadModule(const std::wstring& moduldPath);

        FORCE_INLINE void GetSubFiles(std::wstring path, std::vector<std::wstring>& files,bool isincludedir,bool isincludefile)
        {
#ifdef _WINDOWS
            //文件句柄  
            intptr_t   hFile = 0;
            //文件信息  
            struct _wfinddata_t fileinfo;
            std::wstring p;
            if ((hFile = _wfindfirst(p.assign(path).append(L"\\*").c_str(), &fileinfo)) != (intptr_t)-1)
            {
                do
                {
                    //如果是目录,迭代之  
                    //如果不是,加入列表  
                    if ((fileinfo.attrib & _A_SUBDIR))
                    {
                        if(isincludedir)
                            files.push_back(fileinfo.name);

                    }
                    else 
                    {
                        if(isincludefile)
                            files.push_back(fileinfo.name);

                    }
                } while (_wfindnext(hFile, &fileinfo) == 0);
                _findclose(hFile);
            }
#endif

        }

    }

    class GCMP_DEV_SERVICE_EXPORT FileInfo
    {
    public:
        FileInfo(const std::wstring &driveOrPath);

    public:
        bool Exists() { return m_exists; }
        bool IsFile() { return m_isFile; }
        bool IsDirectory() { return m_isDirectory; }
        int64_t Size() { return m_size; }

    private:
        bool m_exists;
        bool m_isFile;
        bool m_isDirectory;
        int64_t m_size;
    };

#pragma warning( push )
#pragma warning( disable: 4251 )
    class GCMP_DEV_SERVICE_EXPORT DirectoryEntry
    {
    public:
        enum EmDirectoryEntryType
        {
            DET_NotSupport,
            DET_NormalFile,
            DET_Directory,
        };

    public:
        // 返回文件或目录名
        const std::wstring & GetName() const { return m_name; }
        // 返回全路径名
        std::wstring GetFullName() const { return FileSystem::GetPurePath(m_parentPath + FileSystem::Separator() + m_name); }
        EmDirectoryEntryType GetType() const { return m_type; }
        bool IsFile() const { return (m_type == DET_NormalFile); }
        bool IsDirectory() const { return (m_type == DET_Directory); }

    private:
        std::wstring m_parentPath;
        std::wstring m_name;
        EmDirectoryEntryType m_type;

    private:
        friend class DirectoryIterator;
    };
#pragma warning( pop )

    // 遍历目录的迭代器
    class GCMP_DEV_SERVICE_EXPORT DirectoryIterator
    {
    public:
        DirectoryIterator();
        explicit DirectoryIterator(const std::wstring &path);
        ~DirectoryIterator();

    public:
        const DirectoryEntry &operator*() const;
        const DirectoryEntry *operator->() const;

        DirectoryIterator &operator++();

        bool operator!=(const DirectoryIterator &other);
        bool operator==(const DirectoryIterator &other);

    private:
        bool m_isOpen;
        DirectoryEntry m_currentEntry;
#ifdef _WINDOWS
        intptr_t m_hDir;
        struct _wfinddata_t m_dirData;
#else
        DIR *m_dirData;
#endif
    };
}
